/*******************************************************************************
 * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Michael Scharf (Wind River) - initial API and implementation
 * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend
 *******************************************************************************/
package com.google.eclipse.elt.emulator.model;

/**
 * Maintains a snapshot of an instance of {@link ITerminalTextData}. While the {@link ITerminalTextData} continues
 * changing, the snapshot remains unchanged until the next snapshot is taken by calling
 * {@link #updateSnapshot(boolean)}. This is important, because the {@link ITerminalTextData} might get modified by
 * another thread.
 */
public interface ITerminalTextDataSnapshot extends ITerminalTextDataReadOnly {
  /**
   * This listener gets called when the current snapshot is out of date. Calling
   * {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} will have an effect. Once the
   * {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} method is called, it will not be called until
   * {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} is called and a new snapshot needs to be updated again.
   */
  interface SnapshotOutOfDateListener {
    /**
     * Gets called when the snapshot is out of date. To get the snapshot up to date, call
     * {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}.
     *
     * @param snapshot The snapshot that is out of date.
     */
    void snapshotOutOfDate(ITerminalTextDataSnapshot snapshot);
  }

  void addListener(SnapshotOutOfDateListener listener);

  void removeListener(SnapshotOutOfDateListener listener);

  /**
   * Ends the listening to the {@link ITerminalTextData}. After this has been called no new snapshot data is collected.
   */
  void detach();

  boolean isOutOfDate();

  /**
   * The window of interest is the region the snapshot should track. Changes outside this region are ignored. The change
   * takes effect after an update.
   * @param startLine -1 means track the end of the data.
   * @param size number of lines to track. A size of -1 means track all.
   */
  void setInterestWindow(int startLine, int size);

  int getInterestWindowStartLine();

  int getInterestWindowSize();

  /**
   * Create a new snapshot of the {@link ITerminalTextData}. It will efficiently copy the data of the
   * {@link ITerminalTextData} into an internal representation. The snapshot also keeps track of the changes since the
   * previous snapshot.
   * <p>
   * With the methods {@link #getFirstChangedLine()}, {@link #getLastChangedLine()} and {@link #hasLineChanged(int)} you
   * can find out what has changed in the current snapshot since the previous snapshot.
   * </p>
   * <p>
   * If {@code detectScrolling} is {@code true} the information about scrolling can be retrieved using the following methods:
   * {@link #getScrollWindowStartLine()}, {@link #getScrollWindowSize()} and {@link #getScrollWindowShift()}.
   * </p>
   * <p>
   * <b>Note:</b> The method {@link #hasLineChanged(int)} returns changes <b>after</b> the scrolling has been applied.
   * </p>
   *
   * @param detectScrolling indicates whether the snapshot should try to identify scroll changes since the last
   *        snapshot.
   */
  void updateSnapshot(boolean detectScrolling);

  /**
   * Returns the first line changed in this snapshot compared to the previous snapshot.
   * <p>
   * <b>Note:</b> If no line has changed, this returns {@link Integer#MAX_VALUE}.
   * <p>
   * <b>Note:</b> if {@link #updateSnapshot(boolean)} has been called with <code>true</code>, then this does not include
   * lines that only have been scrolled. This is the first line that has changed <b>after</b> the scroll has been
   * applied.
   *
   * @return the first line changed in this snapshot compared to the previous snapshot.
   */
  int getFirstChangedLine();

  /**
   * Returns the last line changed in this snapshot compared to the previous snapshot. If the height has changed since
   * the last update of the snapshot, then the returned value is within the new dimensions.
   * <p>
   * <b>Note:</b> If no line has changed, this returns <code>-1</code>
   * <p>
   * <b>Note:</b> if {@link #updateSnapshot(boolean)} has been called with <code>true</code>, then this does not include
   * lines that only have been scrolled. This is the last line that has changed <b>after</b> the scroll has been
   * applied.
   * <p>
   * A typical for loop using this method would look like this (note the <code>&lt;=</code> in the for loop):
   * <pre>
   * for (int line = snap.{@link #getFirstChangedLine()}; line <b>&lt;=</b> snap.getLastChangedLine(); line++)
   *    if(snap.{@link #hasLineChanged(int) hasLineChanged(line)})
   *       doSomething(line);
   * </pre>
   * @return the last line changed in this snapshot compared to the previous snapshot.
   */
  int getLastChangedLine();

  boolean hasLineChanged(int line);

  boolean hasDimensionsChanged();

  boolean hasTerminalChanged();

  /**
   * If {@link #updateSnapshot(boolean)} was called with {@code true}, then this method returns the top of the scroll
   * region.
   *
   * @return The first line scrolled in this snapshot compared to the previous snapshot.
   *
   * @see ITerminalTextData#scroll(int, int, int)
   */
  int getScrollWindowStartLine();

  /**
   * If {@link #updateSnapshot(boolean)} was called with {@code true}, then this method returns the size of the scroll
   * region. If nothing has changed, 0 is returned.
   *
   * @return The number of lines scrolled in this snapshot compared to the previous snapshot.
   *
   * @see ITerminalTextData#scroll(int, int, int)
   */
  int getScrollWindowSize();

  /**
   * If {@link #updateSnapshot(boolean)} was called with {@code true}, then this method returns number of lines moved by
   * the scroll region.
   *
   * @return The the scroll shift of this snapshot compared to the previous snapshot.
   *
   * @see ITerminalTextData#scroll(int, int, int)
   */
  int getScrollWindowShift();

  ITerminalTextData getTerminalTextData();
}
